Option Explicit

Private Const HASH_CODE_RANDOM_SIZE As Integer = 15

'################################################################
' Descriptor
' 能够保存插入顺序的 Map
'################################################################

'################################################################
' Field
'################################################################
' 保存 key 及其对应的随机值(hashCode)
' Map<key, 随机值字符串>
Private ks As Object

' 保存 key 的顺序
' key = 随机值字符串
Private linkedKs As Collection

' 保存所有的 key-value
' Map<key, value>
Private entries As Object

'#######################################
' Constructor
'#######################################
Private Sub Class_Initialize()
    Set ks = UtilsCreateSysObj.createMap
    Set linkedKs = New Collection
    Set entries = UtilsCreateSysObj.createMap
End Sub

Private Sub Class_Terminate()
    Set ks = Nothing
    Set linkedKs = Nothing
    Set entries = Nothing
End Sub

'################################################################
' Function
'################################################################
' 获取指定 key 下的数据
' @param k key
Public Function Item(ByRef k As Variant) As Variant
    If entries.Exists(k) Then
        ' 获取 key 对应的指针，然后获取 Entry
On Error GoTo catch
        Item = entries.Item(k)
        GoTo continue
catch:
        Err.Clear
        Set Item = entries.Item(k)
continue:
    End If
End Function

' 添加一个键值对
' @param k key
' @param v value
Public Sub Add(ByRef k As Variant, ByRef v As Variant)
    ' 检查内部是否存在指定的 key
    If entries.Exists(k) Then
        ' 如果 key 已经存在了，则删除原始 value 后在重新添加
        entries.Remove k
        entries.Add k, v
    Else
        ' 如果不存在指定的 key，则生成随机值(hashCode)后，添加
        Dim hashCodeStr As String
        hashCodeStr = UtilsRandom.enNumStr(HASH_CODE_RANDOM_SIZE)

'-------------
        ' 防止碰撞
whileStart:
        While True
On Error GoTo catch
            ' 如发生碰撞（能够从顺序缓存中获取到），则生成一个新的 hash 值
            linkedKs.Item hashCodeStr
            hashCodeStr = UtilsRandom.enNumStr(HASH_CODE_RANDOM_SIZE)
            Goto whileStart
catch:
            Err.Clear
            Resume whileEnd
        Wend
whileEnd:
'-------------

        ks.Add k, hashCodeStr
        linkedKs.Add k, hashCodeStr
        entries.Add k, v
    End If
End Sub

' 检查指定 key 是否在当前 Map 中
Public Function Exists(ByRef k As Variant) As Boolean
    Exists = entries.Exists(k)
End Function

' 返回 Map 中所有 value 的数组
Public Function Items() As Variant()
    Dim result() As Variant
    If entries.Count = 0 Then
        ReDim result(0 To -1)
    Else
        ReDim result(0 To entries.Count - 1)

        Dim i As Integer
        For i = 0 To entries.count - 1
On Error GoTo catch
            result(i) = entries.Item(linkedKs.Item(i + 1))
            GoTo continue
catch:
            Err.Clear
            Set result(i) = entries.Item(linkedKs.Item(i + 1))
            Resume continue
continue:
        Next
    End If

    Items = result
End Function

' 返回 Map 中所有 key 的数组
Public Function Keys() As Variant()
    Keys = UtilsCollection.toVariantArray(linkedKs)
End Function

' 从 Map 中删除指定的 key 及其对应的键值对对象
Public Sub Remove(ByRef k As Variant)
    If entries.Exists(k) Then
        linkedKs.Remove ks.Item(k)
        ks.Remove k
        entries.Remove k
    End If
End Sub

' 清空当前 Map 中缓存的所有内容
Public Sub RemoveAll()
    entries.RemoveAll
    UtilsCollection.removeAllCollection linkedKs
    entries.RemoveAll
End Sub

' 返回当前 map 中保存的键值对数量
Public Function Count() As Integer
    Count = entries.count
End Function